import Redis from 'ioredis';
import * as logger from './logger';

class RedisClientClass {
  private client: Redis;
  private readonly KEY_PREFIX = 'app:';

  constructor() {
    this.client = new Redis({
      host: process.env.REDIS_HOST || 'localhost',
      port: parseInt(process.env.REDIS_PORT || '6379', 10),
      password: process.env.REDIS_PASSWORD,
      retryStrategy: (times: number) => {
        const delay = Math.min(times * 50, 2000);
        return delay;
      }
    });

    this.client.on('error', (err: Error) => {
      logger.error('Redis Client Error:', { message: err.message });
    });

    this.client.on('connect', () => {
      logger.info('Redis Client Connected');
    });
  }

  private getFullKey(key: string): string {
    return `${this.KEY_PREFIX}${key}`;
  }

  async get(key: string): Promise<string | null> {
    try {
      return await this.client.get(this.getFullKey(key));
    } catch (error) {
      logger.error('Redis get error:', { error });
      return null;
    }
  }

  async set(key: string, value: string, expireSeconds?: number): Promise<void> {
    try {
      const fullKey = this.getFullKey(key);
      if (expireSeconds) {
        await this.client.set(fullKey, value, 'EX', expireSeconds);
      } else {
        await this.client.set(fullKey, value);
      }
    } catch (error) {
      logger.error('Redis set error:', { error });
    }
  }

  async del(key: string): Promise<void> {
    try {
      await this.client.del(this.getFullKey(key));
    } catch (error) {
      logger.error('Redis del error:', { error });
    }
  }

  async incr(key: string): Promise<number> {
    const fullKey = this.getFullKey(key);
    try {
      let value = await this.client.get(fullKey);
      const newValue = (parseInt(value || '0', 10) + 1).toString();
      await this.client.set(fullKey, newValue);
      return parseInt(newValue, 10);
    } catch (error) {
      logger.error('Redis incr error:', { error });
      return 0;
    }
  }

  async expire(key: string, seconds: number): Promise<void> {
    const fullKey = this.getFullKey(key);
    try {
      const value = await this.client.get(fullKey);
      if (value !== null) {
        await this.client.set(fullKey, value, 'EX', seconds);
      }
    } catch (error) {
      logger.error('Redis expire error:', { error });
    }
  }

  async ttl(key: string): Promise<number> {
    try {
      const expireTime = await this.get(`${key}:expireAt`);
      if (!expireTime) {
        return 0;
      }

      const now = Math.floor(Date.now() / 1000);
      const ttl = parseInt(expireTime, 10) - now;
      return Math.max(0, ttl);
    } catch (error) {
      logger.error('Redis ttl error:', { error });
      return 0;
    }
  }

  async disconnect(): Promise<void> {
    try {
      await this.client.quit();
      logger.info('Redis Client Disconnected');
    } catch (error) {
      logger.error('Redis disconnect error:', { error });
    }
  }
}

export const RedisClient = new RedisClientClass();
